package com.ruoyi.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.freewayso.image.combiner.ImageCombiner;
import com.ruoyi.common.utils.ImageCombineUtils;
import com.ruoyi.core.domain.MsgReplyRule;
import com.ruoyi.core.domain.bo.WxQrCodeBo;
import com.ruoyi.core.domain.vo.WxQrCodeVo;
import com.ruoyi.core.service.IMsgReplyRuleService;
import com.ruoyi.core.service.IWxQrCodeService;
import com.ruoyi.core.service.MsgReplyService;
import com.ruoyi.gpt.domain.WxMsg;
import com.ruoyi.gpt.domain.bo.GptUserBo;
import com.ruoyi.gpt.domain.bo.WxMsgBo;
import com.ruoyi.gpt.domain.vo.GptUserVo;
import com.ruoyi.gpt.service.IGptUserService;
import com.ruoyi.gpt.service.IWxMsgService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.material.WxMpMaterial;
import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 微信公众号消息处理
 * 官方文档：https://developers.weixin.qq.com/doc/offiaccount/Message_Ma nagement/Service_Center_messages.html#7
 * 参考WxJava客服消息文档：https://github.com/Wechat-Group/WxJava/wiki/MP_主动发送消息（客服消息）
 */
@Service
//@RequiredArgsConstructor
@Slf4j
public class MsgReplyServiceImpl implements MsgReplyService {
    @Autowired
    IMsgReplyRuleService msgReplyRuleService;
    @Autowired
    WxMpService wxMpService;
    @Value("${wx.mp.autoReplyInterval:1000}")
    Long autoReplyInterval;
    @Autowired
    IWxMsgService wxMsgService;

    @Autowired
    IWxQrCodeService wxQrCodeService;
    @Autowired
    RedisTemplate<String,String> redisTemplate;

    @Autowired
    @Qualifier("scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService; // 延时任务

    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private IGptUserService gptUserService;

    @Value("${file.upload.url}")
    private String uploadFilePath;

    @Value("${file.mapper.url}")
    private String uploadMapperFilePath;


    /**
     * 根据规则配置通过微信客服消息接口自动回复消息
     *
     *
     * @param appid 公众号appid
     * @param exactMatch 是否精确匹配
     * @param toUser     用户openid
     * @param keywords   匹配关键词
     * @return 是否已自动回复，无匹配规则则不自动回复
     */
    @Override
    public boolean tryAutoReply(String appid, boolean exactMatch, String toUser, String keywords) {
        try {
            // 点击 获取推广码
            if("share".equals(keywords)){
                threadPoolTaskExecutor.execute(() -> {
                    wxMpService.switchover(appid);
                    // 生成带参二维码
                    try {
                        //查询是否该用户，没有就保存起来
                        GptUserVo byOpenIdGptUserVo = gptUserService.getByOpenIdAndAppid(toUser,appid);
                        String mediaId = "";
                        //先从数据库里获取
                        WxQrCodeVo wxQrCodeVo = wxQrCodeService.queryByOpenIdAndAppid(toUser, appid);
                        if(ObjectUtil.isNull(wxQrCodeVo)){
                            //没有数据，直接生成
                            WxQrCodeBo qrCode = createQrCode(appid, toUser);
                            mediaId = qrCode.getMediaId();
                            log.info("生成二维码完成，得到的mediaId为：{}",mediaId);
                            wxQrCodeService.insertByBoAsync(qrCode); // 存到数据库
                        }else{
                            // 有数据，判断是否过期
                            Date expireTime = wxQrCodeVo.getExpireTime();
                            long expireTimeTime = expireTime.getTime() / 1000;
                            long now = new Date().getTime() / 1000;
                            if(expireTimeTime-now>86400){
                                // 过期时间至少有一天，直接使用
                                mediaId = wxQrCodeVo.getMediaId();
                            }else {
                                // 过期时间不足一天，重新生成，并更新
                                // 先删除之前本地的二维码
                                String oldWxQrCodePath = wxQrCodeVo.getRemark();
                                try {
                                    FileUtil.del(new File(oldWxQrCodePath));
                                } catch (IORuntimeException e) {
                                    log.error("删除过期二维码错误"+e.getMessage());
                                }
                                WxQrCodeBo qrCode = createQrCode(appid, toUser);
                                wxQrCodeVo.setExpireTime(qrCode.getExpireTime());
                                wxQrCodeVo.setTicket(qrCode.getTicket());
                                wxQrCodeVo.setUrl(qrCode.getUrl());// 对应本地访问的url
                                wxQrCodeVo.setMediaId(qrCode.getMediaId());
                                wxQrCodeVo.setMediaUrl(qrCode.getMediaUrl());
                                wxQrCodeVo.setRemark(qrCode.getRemark());
                                // 更新二维码
                                wxQrCodeService.updateByBo(BeanUtil.toBean(wxQrCodeVo,WxQrCodeBo.class));
                                mediaId = qrCode.getMediaId();
                            }
                        }
                        if(ObjectUtil.isNull(byOpenIdGptUserVo)){
                            // 证明是新用户 ，需要生成二维码
                            byOpenIdGptUserVo.setOpenid(toUser);
                            byOpenIdGptUserVo.setAppid(appid);
                            gptUserService.insertByBo(BeanUtil.toBean(byOpenIdGptUserVo, GptUserBo.class));
                        }

                        if(mediaId.length()>0){
                            this.reply(toUser,"image",mediaId);
                            this.reply(toUser,"text","分享二维码获取成功，每通过该二维码关注一个用户，您就增加5次体验次数");
                        }else {
                            this.reply(toUser,"text","分享二维码获取失败");
                        }

                    } catch (Exception e) {
                        log.error("生成带参二维码失败{}",e.getMessage());
                        this.reply(toUser,"text","分享二维码获取失败");
                    }
                });
                return true;
            }
            // 如果是新用户扫描
            if(keywords.startsWith("qrscene_")){
                String openid = StrUtil.removePrefix(keywords, "qrscene_");
                if(openid.length() > 0){
                    // 查询分享者信息
                    GptUserVo gptUserVo = gptUserService.getByOpenIdAndAppid(openid,appid);
                    GptUserVo scanGptUserVo = gptUserService.getByOpenIdAndAppid(toUser, appid);
                    // 能查到分享者，并且不是自己扫码，数据库中没有扫码人信息
                    if(ObjectUtil.isNotNull(gptUserVo) && !openid.equals(toUser) && ObjectUtil.isNull(scanGptUserVo)){
                        // 不为空，给分享者增加体验次数
                        gptUserVo.setGptRemaining(gptUserVo.getGptRemaining() + 5);
                        gptUserVo.setShareTimes(gptUserVo.getShareTimes()+1);
                        gptUserService.updateByBo(BeanUtil.toBean(gptUserVo, GptUserBo.class));
                        this.reply(gptUserVo.getOpenid(),"text","您分享的二维码被新用户关注，您增加5次体验次数");

                        // 将扫码人存到数据库
                        scanGptUserVo = new GptUserVo();
                        scanGptUserVo.setAppid(appid);
                        scanGptUserVo.setOpenid(toUser);
                        scanGptUserVo.setShareBy(openid);
                        scanGptUserVo.setIsFollow("1");
                        gptUserService.insertByBo(BeanUtil.toBean(scanGptUserVo,GptUserBo.class));
                        this.reply(toUser,"text","您通过分享二维码关注了公众号，获得5次体验次数");

                    }
                }
            }
            List<MsgReplyRule> rules = msgReplyRuleService.getMatchedRules(appid,exactMatch, keywords);
            if (rules.isEmpty()) {
                return false;
            }
            long delay = 0;
            for (MsgReplyRule rule : rules) {
                this.reply(toUser,rule.getReplyType(),rule.getReplyContent());
                scheduledExecutorService.schedule(() -> {
                    wxMpService.switchover(appid);
                    this.reply(toUser,rule.getReplyType(),rule.getReplyContent());
                }, delay, TimeUnit.MILLISECONDS);
                delay += autoReplyInterval;
            }
            return true;
        } catch (Exception e) {
            log.error("自动回复出错：", e);
        }
        return false;
    }

    /**
     *  生成带参数二维码
     */
    private WxQrCodeBo createQrCode(String appid,String openid) throws Exception {
        log.info("开始生成分享二维码");
        int expireSeconds = 2592000;
        Date expireTime =  new Date(System.currentTimeMillis() + expireSeconds * 1000L);
        String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=";
        String fileName = IdUtil.fastSimpleUUID() + ".png";
        String newPath = uploadFilePath + "images" + File.separator +fileName;
        String newMapperPath = uploadMapperFilePath +"images" + File.separator + fileName;
        WxQrCodeBo wxQrCodeBo = new WxQrCodeBo();
        wxQrCodeBo.setAppid(appid);
        wxQrCodeBo.setOpenid(openid);
        wxQrCodeBo.setSceneStr(openid);
        wxQrCodeBo.setExpireTime(expireTime);
        wxQrCodeBo.setRemark(newPath);
        // 生成 临时带参数二维码
        WxMpQrCodeTicket wxMpQrCodeTicket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(wxQrCodeBo.getSceneStr(), expireSeconds);
        String ticket = wxMpQrCodeTicket.getTicket();
        String encode = URLEncoder.encode(ticket);
        String ticketUrl = url + ticket;
        ImageCombiner combiner = ImageCombineUtils.createSharePoster(ticketUrl, "欢迎体验亚尔科技AI", "前往公众号生成自己的分享海报，每分享成功一个增加5次体验");
        combiner.save(newPath);
        wxQrCodeBo.setTicket(ticket);
        wxQrCodeBo.setUrl(newMapperPath); // 对应本地访问的url
        File file = new File(newPath);
        // 上传媒体素材
        log.info("上传媒体素材：{}",fileName);
        WxMpMaterial wxMaterial = new WxMpMaterial();
        wxMaterial.setFile(file);
        wxMaterial.setName(fileName);
        WxMpMaterialUploadResult res = wxMpService.getMaterialService().materialFileUpload("image",wxMaterial);
        String mediaId = res.getMediaId();
        String mediaUrl = res.getUrl();
        wxQrCodeBo.setMediaUrl(mediaUrl);
        wxQrCodeBo.setMediaId(mediaId);
        wxQrCodeBo.setShareCount("0");
        return wxQrCodeBo;
    }


    @Override
    public void replyText(String toUser, String content) throws WxErrorException {
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.TEXT().toUser(toUser).content(content).build());
        if("分享二维码获取失败".equals(content)){
            return;
        }
        JSONObject json = new JSONObject().putOnce("content",content);
        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.TEXT,toUser,json), WxMsgBo.class));
    }

    @Override
    public void replyImage(String toUser, String mediaId) throws WxErrorException {
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.IMAGE().toUser(toUser).mediaId(mediaId).build());

        JSONObject json = new JSONObject().putOnce("mediaId",mediaId);
        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.IMAGE,toUser,json),WxMsgBo.class));
    }

    @Override
    public void replyVoice(String toUser, String mediaId) throws WxErrorException {
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.VOICE().toUser(toUser).mediaId(mediaId).build());

        JSONObject json = new JSONObject().putOnce("mediaId",mediaId);
        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.VOICE,toUser,json),WxMsgBo.class));
    }

    @Override
    public void replyVideo(String toUser, String mediaId) throws WxErrorException {
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.VIDEO().toUser(toUser).mediaId(mediaId).build());

        JSONObject json = new JSONObject().putOnce("mediaId",mediaId);
        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.VIDEO,toUser,json),WxMsgBo.class));
    }

    @Override
    public void replyMusic(String toUser, String musicInfoJson) throws WxErrorException {
        JSONObject json = JSONUtil.parseObj(musicInfoJson);
        wxMpService.getKefuService().sendKefuMessage(
            WxMpKefuMessage.MUSIC().toUser(toUser)
                .musicUrl(json.getStr("musicurl"))
                .hqMusicUrl(json.getStr("hqmusicurl"))
                .title(json.getStr("title"))
                .description(json.getStr("description"))
                .thumbMediaId(json.getStr("thumb_media_id"))
                .build());

        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.IMAGE,toUser,json),WxMsgBo.class));
    }

    /**
     * 发送图文消息（点击跳转到外链） 图文消息条数限制在1条以内
     * @param toUser
     * @param newsInfoJson
     * @throws WxErrorException
     */
    @Override
    public void replyNews(String toUser, String newsInfoJson) throws WxErrorException {
        JSONObject entries = JSONUtil.parseObj(newsInfoJson);
        WxMpKefuMessage.WxArticle wxArticle = BeanUtil.toBean(entries, WxMpKefuMessage.WxArticle.class);
//        WxMpKefuMessage.WxArticle wxArticle = JSON.parseObject(newsInfoJson, WxMpKefuMessage.WxArticle.class);
        List<WxMpKefuMessage.WxArticle> newsList = new ArrayList<WxMpKefuMessage.WxArticle>(){{add(wxArticle);}};
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.NEWS().toUser(toUser).articles(newsList).build());

        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.NEWS,toUser,JSONUtil.parseObj(newsInfoJson)),WxMsgBo.class));
    }

    /**
     * 发送图文消息（点击跳转到图文消息页面） 图文消息条数限制在1条以内
     * @param toUser
     * @param mediaId
     * @throws WxErrorException
     */
    @Override
    public void replyMpNews(String toUser, String mediaId) throws WxErrorException {
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.MPNEWS().toUser(toUser).mediaId(mediaId).build());

        JSONObject json = new JSONObject().putOnce("mediaId",mediaId);
        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.MPNEWS,toUser,json),WxMsgBo.class));
    }

    @Override
    public void replyWxCard(String toUser, String cardId) throws WxErrorException {
        wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage.WXCARD().toUser(toUser).cardId(cardId).build());

        JSONObject json = new JSONObject().putOnce("cardId",cardId);
        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.WXCARD,toUser,json),WxMsgBo.class));
    }

    @Override
    public void replyMiniProgram(String toUser, String miniProgramInfoJson) throws WxErrorException {
        JSONObject json = JSONUtil.parseObj(miniProgramInfoJson);
        wxMpService.getKefuService().sendKefuMessage(
            WxMpKefuMessage.MINIPROGRAMPAGE()
                .toUser(toUser)
                .title(json.getStr("title"))
                .appId(json.getStr("appid"))
                .pagePath(json.getStr("pagepath"))
                .thumbMediaId(json.getStr("thumb_media_id"))
                .build());

        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.IMAGE,toUser,json),WxMsgBo.class));
    }

    @Override
    public void replyMsgMenu(String toUser, String msgMenusJson) throws WxErrorException {
        JSONObject json = JSONUtil.parseObj(msgMenusJson);
        JSONArray jsonArray = JSONUtil.parseArray(json.getStr("list"));
        List<WxMpKefuMessage.MsgMenu> msgMenus = new ArrayList<>();
        for (Object o : jsonArray) {
            msgMenus.add(BeanUtil.toBean(o,WxMpKefuMessage.MsgMenu.class));
        }
//        List<WxMpKefuMessage.MsgMenu> msgMenus = JSONUtil.parseArray(json.getStr("list"),WxMpKefuMessage.MsgMenu.class);
        wxMpService.getKefuService().sendKefuMessage(
            WxMpKefuMessage.MSGMENU()
                .toUser(toUser)
                .headContent(json.getStr("head_content"))
                .tailContent(json.getStr("tail_content"))
                .msgMenus(msgMenus).build());

        wxMsgService.insertByBoAsync(BeanUtil.toBean(WxMsg.buildOutMsg(WxConsts.KefuMsgType.IMAGE,toUser,json),WxMsgBo.class));
    }



}
